﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace fap
{
    /// <summary>
    ///     曲线图  CurveChart.xaml 的交互逻辑
    /// </summary>
    public partial class CurveChart
    {
        // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof (string), typeof (CurveChart), new PropertyMetadata(""));

        private readonly TextBlock _tooltipTxt = new TextBlock();


        private readonly double bottomMargin = 60;
        private readonly double leftMargin = 40;
        private readonly double rightMargin = 50;
        private readonly double topMargin = 40;
        private Color _gridLineColor = Colors.AliceBlue;
        private List<Tuple<Point, string>> _itemsSource = new List<Tuple<Point, string>>();

        private Visibility _legendVisibility = Visibility.Visible;
        private Brush _lineBrush = Brushes.Red;
        private bool _showGridLine = true;

        private readonly TextBlock _tbx = new TextBlock {FontSize = 10, MaxWidth = 90, TextWrapping = TextWrapping.Wrap};
        private Brush _textColor = Brushes.Yellow;

        private Brush _titleColor = Brushes.Black;

        private Visibility _titleVisibility = Visibility.Visible;

        /// <summary>
        ///     曲线图
        /// </summary>
        public CurveChart()
        {
            InitializeComponent();
        }

        /// <summary>
        ///     x轴刻度倾斜角度
        /// </summary>
        public double XAxisMarkSkewAngle { get; set; }

        /// <summary>
        ///     x轴单位
        /// </summary>
        public string XAxisUnit { get; set; }

        /// <summary>
        ///     y轴单位
        /// </summary>
        public string YAxisUnit { get; set; }

        /// <summary>
        ///     数据源，存两个变量，point点坐标，string字符提示
        /// </summary>
        public List<Tuple<Point, string>> ItemsSource
        {
            get { return _itemsSource; }
            set
            {
                _itemsSource = value;
                Refresh();
            }
        }

        /// <summary>
        ///     x轴最大值
        /// </summary>
        public double MaxX { get; set; }

        /// <summary>
        ///     y轴最大值
        /// </summary>
        public double MaxY { get; set; }

        /// <summary>
        ///     x轴最小值
        /// </summary>
        public double MinX { get; set; }

        /// <summary>
        ///     y轴最小值
        /// </summary>
        public double MinY { get; set; }

        /// <summary>
        ///     x轴刻度间隔步长
        /// </summary>
        public double XStep { get; set; }

        /// <summary>
        ///     y轴刻度间隔步长
        /// </summary>
        public double YStep { get; set; }

        /// <summary>
        ///     水平线颜色
        /// </summary>
        public Color GridLineColor
        {
            get { return _gridLineColor; }
            set { _gridLineColor = value; }
        }


        /// <summary>
        ///     头部标题
        /// </summary>
        public string Title
        {
            get { return (string) GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }


        /// <summary>
        ///     显示网格线
        /// </summary>
        public bool ShowGridLine
        {
            get { return _showGridLine; }
            set { _showGridLine = value; }
        }

        /// <summary>
        ///     文字颜色
        /// </summary>
        public Brush TextColor
        {
            get { return _textColor; }
            set { _textColor = value; }
        }

        /// <summary>
        ///     标题颜色
        /// </summary>
        public Brush TitleColor
        {
            get { return _titleColor; }
            set { _titleColor = value; }
        }

        /// <summary>
        ///     标题可见度
        /// </summary>
        public Visibility TitleVisibility
        {
            get { return _titleVisibility; }
            set { _titleVisibility = value; }
        }


        /// <summary>
        ///     线条颜色
        /// </summary>
        public Brush LineBrush
        {
            get { return _lineBrush; }
            set { _lineBrush = value; }
        }

        /// <summary>
        ///     图例可见度
        /// </summary>
        public Visibility LegendVisibility
        {
            get { return _legendVisibility; }
            set { _legendVisibility = value; }
        }


        private void BarChart_OnLoaded(object sender, RoutedEventArgs e)
        {
            Refresh();
        }

        private void BarChart_OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            Refresh();
        }


        public void Refresh()
        {
            try
            {
                if (!IsLoaded)
                {
                    return;
                }
                chartArea.Children.Clear();
                _tooltipTxt.Visibility = Visibility.Collapsed;
                chartArea.Children.Add(_tooltipTxt);
                if (ItemsSource != null && ItemsSource.Any())
                {
                    //隐藏“无数据”提示
                    txtNoData.Visibility = Visibility.Hidden;

                    //获取y最大值
                    if (MaxY < 0.0001)
                    {
                        MaxY = ItemsSource.Max(_ => _.Item1.Y);
                    }
                    if (MaxX < 0.0001)
                    {
                        MaxX = ItemsSource.Max(_ => _.Item1.X);
                    }
                    if (Math.Abs(MaxX) < 0.000001 || Math.Abs(MaxY) < 0.000001)
                    {
                        return;
                    }

                    chartArea.Width = chartGrid.ActualWidth - SystemParameters.VerticalScrollBarWidth;
                    chartArea.Height = chartGrid.ActualHeight - SystemParameters.HorizontalScrollBarHeight;
                    DrawXAxisTicks();
                    DrawAxis();
                    DrawYAxisTicks();
                    DrawPolyline();
                    DrawUnit();
                    DrawMarker();
                }
                else
                {
                    txtNoData.Visibility = Visibility.Visible;
                }
            }
            catch (Exception ex)
            {
                chartArea.Children.Clear();
                txtNoData.Visibility = Visibility.Visible;
                txtNoData.Text = ex.Message;
            }
        }

        /// <summary>
        ///     画轴上刻度单位
        /// </summary>
        private void DrawUnit()
        {
            var textblockX = new TextBlock
            {
                Text = XAxisUnit,
                Foreground = Brushes.Gray,
                HorizontalAlignment = HorizontalAlignment.Right
            };
            Canvas.SetRight(textblockX, 0);
            Canvas.SetTop(textblockX, chartArea.Height - bottomMargin);
            chartArea.Children.Add(textblockX);
            var textblockY = new TextBlock {Text = YAxisUnit, Foreground = Brushes.Gray};
            var transY = new SkewTransform(0, 0);
            textblockY.RenderTransform = transY;
            Canvas.SetLeft(textblockY, 0);
            Canvas.SetTop(textblockY, 0);
            chartArea.Children.Add(textblockY);
        }

        /// <summary>
        ///     画y轴刻度和文字，并且画水平线
        /// </summary>
        private void DrawYAxisTicks()
        {
            if (MinY >= MaxY)
            {
                return;
            }
            if (YStep < 0.0001)
            {
                YStep = (MaxY - MinY)/10;
            }

            for (var i = MinY; i <= MaxY+0.01; i += YStep)
            {
                var y = chartArea.Height - bottomMargin -
                        (i - MinY)*(chartArea.Height - bottomMargin - topMargin)/(MaxY - MinY);
                var marker = new Line
                {
                    X1 = leftMargin - 5,
                    Y1 = y,
                    X2 = leftMargin,
                    Y2 = y,
                    Stroke = Brushes.Red
                };

                chartArea.Children.Add(marker);

                //画水平网格
                if (ShowGridLine)
                {
                    var gridLine = new Line
                    {
                        X1 = leftMargin,
                        Y1 = y,
                        X2 = chartArea.Width - rightMargin,
                        Y2 = y,
                        StrokeThickness = 1,
                        Stroke = new SolidColorBrush(GridLineColor)
                    };
                    chartArea.Children.Add(gridLine);
                }

                //画y轴字符
                var markText = new TextBlock
                {
                    Text = i.ToString(CultureInfo.InvariantCulture),
                    Width = 30,
                    Foreground = TextColor,
                    FontSize = 10,
                    HorizontalAlignment = HorizontalAlignment.Right,
                    TextAlignment = TextAlignment.Right
                };
                chartArea.Children.Add(markText);

                Canvas.SetTop(markText, y - 10);
                Canvas.SetLeft(markText, leftMargin - 40);
            }
        }

        /// <summary>
        ///     画x轴和y轴
        /// </summary>
        private void DrawAxis()
        {
            var xaxis = new Line
            {
                X1 = leftMargin,
                Y1 = chartArea.Height - bottomMargin,
                X2 = chartArea.Width - rightMargin,
                Y2 = chartArea.Height - bottomMargin,
                Stroke = new SolidColorBrush(Colors.Black)
            };

            chartArea.Children.Add(xaxis);


            var yaxis = new Line
            {
                X1 = leftMargin,
                Y1 = topMargin,
                X2 = leftMargin,
                Y2 = chartArea.Height - bottomMargin,
                Stroke = new SolidColorBrush(Colors.Black)
            };
            chartArea.Children.Add(yaxis);
        }

        /// <summary>
        ///     画x轴标签
        /// </summary>
        private void DrawXAxisTicks()
        {
            if (MinX >= MaxX)
            {
                return;
            }
            if (XStep < 0.0001)
            {
                XStep = (MaxX - MinX)/10;
            }
            for (var i = MinX; i <= MaxX + 0.01; i += XStep)
            {
                var x = leftMargin + (i - MinX)*(chartArea.Width - leftMargin - rightMargin)/(MaxX - MinX);
                var marker = new Line
                {
                    X1 = x,
                    Y1 = chartArea.Height - bottomMargin,
                    X2 = x,
                    Y2 = chartArea.Height - bottomMargin + 4,
                    Stroke = Brushes.Red
                };
                chartArea.Children.Add(marker);
                //画水平网格
                if (ShowGridLine)
                {
                    var gridLine = new Line
                    {
                        X1 = x,
                        Y1 = chartArea.Height - bottomMargin,
                        X2 = x,
                        Y2 = topMargin,
                        StrokeThickness = 1,
                        Stroke = new SolidColorBrush(GridLineColor)
                    };
                    chartArea.Children.Add(gridLine);
                }
                //画x轴字符
                var text = i.ToString(CultureInfo.InvariantCulture);
                var markText = new TextBlock
                {
                    Text = text,
                    Width = 130,
                    Foreground = TextColor,
                    VerticalAlignment = VerticalAlignment.Top,
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    TextAlignment = TextAlignment.Left,
                    FontSize = 15
                };
                Transform st = new SkewTransform(0, XAxisMarkSkewAngle);
                markText.RenderTransform = st;
                chartArea.Children.Add(markText);
                Canvas.SetTop(markText, chartArea.Height - bottomMargin + 5);
                Canvas.SetLeft(markText, x);
            }
        }


        /// <summary>
        ///     画折线
        /// </summary>
        private void DrawPolyline()
        {
            var polyline = new Polyline();
            foreach (var t in ItemsSource)
            {
                polyline.Points.Add(GetRealPoint(t.Item1));
            }
            polyline.Stroke = LineBrush;
            chartArea.Children.Add(polyline);
        }

        /// <summary>
        ///     添加标记
        /// </summary>
        private void DrawMarker()
        {
            foreach (var pt in ItemsSource)
            {
                var realPoint = GetRealPoint(pt.Item1);
                var ellipse = new Ellipse
                {
                    Fill = LineBrush,
                    Width = 6,
                    Height = 6
                };
                var pt1 = pt;
                ellipse.MouseEnter += delegate
                {
                    ellipse.Fill = Brushes.Red;
                    ShowToolTip(pt1.Item2, realPoint.X + 3, realPoint.Y + 3);
                };
                ellipse.MouseLeave += delegate
                {
                    ellipse.Fill = LineBrush;
                    HideToolTip();
                };
                ellipse.MouseLeftButtonDown += delegate { };
                Canvas.SetLeft(ellipse, realPoint.X - 3);
                Canvas.SetTop(ellipse, realPoint.Y - 3);
                chartArea.Children.Add(ellipse);
            }
        }

        private void HideToolTip()
        {
            if (chartArea.Children.Contains(_tbx))
            {
                chartArea.Children.Remove(_tbx);
            }
        }

        private void ShowToolTip(string p1, double p2, double p3)
        {
            _tbx.Text = p1;
            Canvas.SetLeft(_tbx, p2);
            Canvas.SetTop(_tbx, p3);
            chartArea.Children.Add(_tbx);
        }

        /// <summary>
        ///     计算canvas上坐标
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        private Point GetRealPoint(Point point)
        {
            var realX = leftMargin + (point.X - MinX)*(chartArea.Width - leftMargin - rightMargin)/(MaxX - MinX);
            var realY = topMargin + (MaxY - point.Y)*(chartArea.Height - bottomMargin - topMargin)/(MaxY - MinY);
            return new Point(realX, realY);
        }
    }
}